package com.lvw.weather.city.service.impl;

import com.lvw.weather.city.service.ICityService;
import com.lvw.weather.forcast.web.IForcastAPI;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.ObservableExecutionMode;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheKey;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;
import com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import rx.Observable;

import java.util.List;
import java.util.logging.Logger;
import java.util.stream.Collectors;

/**
 * Hystrix的注解版本
 */
@Service
@Qualifier("cityServiceSimple")
public class CityServiceSimple implements ICityService {

    final Logger log = Logger.getLogger(getClass().toString());

    @Autowired
    RestTemplate restTemplate;

    @Autowired
    IForcastAPI forcastAPI;


    /*
     熔断器的使用:
     1. 缓存处理两个注解
     2. 降级方法和可忽略的异常
     3. 线程池: 通过分组名 / 命令名 / 线程池名 进行分组管理
     */
    @Override
    @CacheResult(cacheKeyMethod = "cityForcastCacheKey")   // 默认使用全部参数作为cacheKey, 也可以通过cacheKeyMethod指定一个方法
    /*@HystrixCommand(fallbackMethod = "cityFallback", ignoreExceptions = NumberFormatException.class)*/   // ignore specific exception
    @HystrixCommand(
            fallbackMethod = "cityFallback",
            groupKey = "defaultGroup",
            commandKey = "cityForcast",
            threadPoolKey = "cityService")
    public String cityForcast(@CacheKey String city) {  // 也可以指定对象中的属性, 例如: (@CacheKey("id") User user) 如果使用了上面cacheKeyMethod, 这里的@CacheKey不会生效.


        String forcast = null;

        /*
        // sync mode with restTemplate
        forcast = restTemplate.getForEntity("http://WEATHER-FORCAST-SERVICE/forcast/" + city, String.class).getBody();
        log.info("----REMOTE RESULT: " + forcast);
         */

        // Use OpenFeign
        forcast = forcastAPI.forcast(city);

        /*
        // Async mode
        Future<String> futureString = new AsyncResult<String>() {
            @Override
            public String invoke() {
                return restTemplate.getForEntity("http://WEATHER-FORCAST-SERVICE/forcast/" + city, String.class).getBody();;
            }
        };
        try {
            forcast = futureString.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }*/


        return forcast;
    }

    /* 批量处理, 注意返回值必须是Future
    @HystrixCollapser(batchMethod = "getAllCityForcast",collapserProperties = {@HystrixProperty(name="timerDelayInMilliseconds", value = "500")})
    public Future<String> cityForcast(String city) {
        return null;
    }

    @HystrixCommand(
            fallbackMethod = "citysFallback",
            groupKey = "defaultGroup",
            commandKey = "cityForcasts",
            threadPoolKey = "cityService")
    public List<String> getAllCityForcast(List<String> citys) {
        System.out.println("======> getAllCityForcast ");
        // sync mode
        List<String> forcast = restTemplate.getForEntity("http://WEATHER-FORCAST-SERVICE/forcast?citys=" + citys, List.class).getBody();
        return forcast;
    }*/

    /*
    演示用的更新方法, 用来演示CacheRemove注解
     */
    @CacheRemove(commandKey = "cityForcast", cacheKeyMethod = "cityForcastCacheKey")  // 必须指定具体的commandKey, 否则找不到请求缓存
    @HystrixCommand(
            fallbackMethod = "cityFallback",
            groupKey = "defaultGroup",
            commandKey = "updateCityForcast",
            threadPoolKey = "cityService")
    public boolean updateCityForcast(@CacheKey String city) {

        // update omitted

        return true;
    }

    String cityForcastCacheKey(String city){
        return city + "_cacheKey";
    }

    public String cityFallback(String city, Throwable throwable) {
        log.info("METHOD cityForcast FALLBACK");
        return "error";
    }

    public List<String> citysFallback(List<String> citys) {
        log.info("FORCAST SERVICE FALLBACK");

        return citys.stream().map(s -> "error").collect(Collectors.toList());
    }

    /**
     * rxjava 版本的注解
     *
     * @param city
     * @return
     */
    @HystrixCommand(observableExecutionMode=ObservableExecutionMode.EAGER)  // EAGER => hot.  LAZY => cold.
    public Observable<String> cityForcastObservable(String city) {
        return Observable.create(subscriber -> {
            if (!subscriber.isUnsubscribed()) {
                String string = restTemplate.getForEntity("http://WEATHER-FORCAST-SERVICE/forcast/" + city, String.class).getBody();
                subscriber.onNext(string);
                subscriber.onCompleted();
            }
        });
    }
}
